use std::ops::{Add, AddAssign, Div, DivAssign, Mul, MulAssign, Neg, Sub, SubAssign};

use serde::{Deserialize, Serialize};

use super::Vector2f;
#[derive(Debug, Default, Clone, Copy, PartialEq, PartialOrd,Serialize, Deserialize)]
pub struct Vector3<T>
where
    T: Copy + Default,
{
    pub x: T,
    pub y: T,
    pub z: T,
}

// Addition
impl<T> Add for Vector3<T>
where
    T: Copy + Default + Add<Output = T>,
{
    type Output = Vector3<T>;

    fn add(self, other: Vector3<T>) -> Vector3<T> {
        Vector3 {
            x: self.x + other.x,
            y: self.y + other.y,
            z: self.z + other.z,
        }
    }
}

// Addition Assign
impl<T> AddAssign for Vector3<T>
where
    T: Copy + Default + Add<Output = T>,
{
    fn add_assign(&mut self, other: Self) {
        *self = Self {
            x: self.x + other.x,
            y: self.y + other.y,
            z: self.z + other.z,
        };
    }
}

// Addition scalar
impl<T> Add<T> for Vector3<T>
where
    T: Copy + Default + Add<Output = T>,
{
    type Output = Vector3<T>;

    fn add(self, other: T) -> Self::Output {
        Vector3 {
            x: self.x + other,
            y: self.y + other,
            z: self.z + other,
        }
    }
}

// Addition scalar Assign
impl<T> AddAssign<T> for Vector3<T>
where
    T: Copy + Default + Add<Output = T>,
{
    fn add_assign(&mut self, other: T) {
        *self = Self {
            x: self.x + other,
            y: self.y + other,
            z: self.z + other,
        };
    }
}

// Subtraction
impl<T> Sub for Vector3<T>
where
    T: Copy + Default + Sub<Output = T>,
{
    type Output = Self;

    fn sub(self, other: Self) -> Self {
        Vector3 {
            x: self.x - other.x,
            y: self.y - other.y,
            z: self.z - other.z,
        }
    }
}

// Subtraction Assign
impl<T> SubAssign for Vector3<T>
where
    T: Copy + Default + Sub<Output = T>,
{
    fn sub_assign(&mut self, other: Self) {
        *self = Self {
            x: self.x - other.x,
            y: self.y - other.y,
            z: self.z - other.z,
        };
    }
}

// Subtraction scalar
impl<T> Sub<T> for Vector3<T>
where
    T: Copy + Default + Sub<Output = T>,
{
    type Output = Self;

    fn sub(self, other: T) -> Self::Output {
        Self {
            x: self.x - other,
            y: self.y - other,
            z: self.z - other,
        }
    }
}

// Subtraction scalar Assign
impl<T> SubAssign<T> for Vector3<T>
where
    T: Copy + Default + Sub<Output = T>,
{
    fn sub_assign(&mut self, other: T) {
        *self = Self {
            x: self.x - other,
            y: self.y - other,
            z: self.z - other,
        };
    }
}

// Multiplication
impl<T> Mul for Vector3<T>
where
    T: Copy + Default + Mul<Output = T>,
{
    type Output = Vector3<T>;

    fn mul(self, other: Vector3<T>) -> Vector3<T> {
        Vector3 {
            x: self.x * other.x,
            y: self.y * other.y,
            z: self.z * other.z,
        }
    }
}

// Multiplication Assign
impl<T> MulAssign for Vector3<T>
where
    T: Copy + Default + Mul<Output = T>,
{
    fn mul_assign(&mut self, other: Self) {
        *self = Self {
            x: self.x * other.x,
            y: self.y * other.y,
            z: self.z * other.z,
        };
    }
}

// Multiplication scalar
impl<T> Mul<T> for Vector3<T>
where
    T: Copy + Default + Mul<Output = T>,
{
    type Output = Self;

    fn mul(self, other: T) -> Self::Output {
        Self {
            x: self.x * other,
            y: self.y * other,
            z: self.z * other,
        }
    }
}

// Multiplication scalar Assign
impl<T> MulAssign<T> for Vector3<T>
where
    T: Copy + Default + Mul<Output = T>,
{
    fn mul_assign(&mut self, other: T) {
        *self = Self {
            x: self.x * other,
            y: self.y * other,
            z: self.z * other,
        };
    }
}

// Division
impl<T> Div for Vector3<T>
where
    T: Copy + Default + Div<Output = T>,
{
    type Output = Self;

    fn div(self, other: Vector3<T>) -> Self::Output {
        Self {
            x: self.x / other.x,
            y: self.y / other.y,
            z: self.z / other.z,
        }
    }
}

// Division Assign
impl<T> DivAssign for Vector3<T>
where
    T: Copy + Default + Div<Output = T>,
{
    fn div_assign(&mut self, other: Vector3<T>) {
        *self = Self {
            x: self.x / other.x,
            y: self.y / other.y,
            z: self.z / other.z,
        };
    }
}

// Division scalar
impl<T> Div<T> for Vector3<T>
where
    T: Copy + Default + Div<Output = T>,
{
    type Output = Self;

    fn div(self, other: T) -> Self::Output {
        Self {
            x: self.x / other,
            y: self.y / other,
            z: self.z / other,
        }
    }
}

// Division scalar Assign
impl<T> DivAssign<T> for Vector3<T>
where
    T: Copy + Default + Div<Output = T> + Neg<Output = T>,
{
    fn div_assign(&mut self, other: T) {
        *self = Self {
            x: self.x / other,
            y: self.y / other,
            z: self.z / other,
        };
    }
}

// Negation
impl<T> Neg for Vector3<T>
where
    T: Copy + Default + Neg<Output = T>,
{
    type Output = Self;

    fn neg(self) -> Self::Output {
        Vector3 {
            x: -self.x,
            y: -self.y,
            z: -self.z,
        }
    }
}

impl<T> Vector3<T>
where
    T: Copy
        + Default
        + Div<Output = T>
        + Mul<Output = T>
        + Add<Output = T>
        + Div<Output = T>
        + Sub<Output = T>,
{
    pub fn new(x: T, y: T, z: T) -> Vector3<T> {
        Vector3 { x, y, z }
    }

    // LengthSquared
    pub fn length_squared(&self) -> T {
        self.x * self.x + self.y * self.y + self.z * self.z
    }

    pub const ZERO: Vector3<f32> = Vector3 {
        x: 0.0,
        y: 0.0,
        z: 0.0,
    };

    // dot product
    pub fn dot(&self, other: &Vector3<T>) -> T {
        self.x * other.x + self.y * other.y + self.z * other.z
    }

    // cross product
    pub fn cross(&self, other: &Vector3<T>) -> Vector3<T> {
        Vector3 {
            x: self.y * other.z - self.z * other.y,
            y: self.z * other.x - self.x * other.z,
            z: self.x * other.y - self.y * other.x,
        }
    }
}

pub type Vector3u = Vector3<u32>;
pub type Vector3i = Vector3<i32>;
pub type Vector3u16 = Vector3<u16>;

#[derive(Debug, Default, Clone, Copy, PartialEq, PartialOrd, Serialize, Deserialize)]
pub struct Vector3f {
    pub x: f32,
    pub y: f32,
    pub z: f32,
}

// Addition
impl Add for Vector3f {
    type Output = Vector3f;

    fn add(self, other: Vector3f) -> Vector3f {
        Vector3f {
            x: self.x + other.x,
            y: self.y + other.y,
            z: self.z + other.z,
        }
    }
}

// Addition Assign
impl AddAssign for Vector3f {
    fn add_assign(&mut self, other: Self) {
        *self = Self {
            x: self.x + other.x,
            y: self.y + other.y,
            z: self.z + other.z,
        };
    }
}

// Addition scalar
impl Add<f32> for Vector3f {
    type Output = Vector3f;

    fn add(self, other: f32) -> Self::Output {
        Vector3f {
            x: self.x + other,
            y: self.y + other,
            z: self.z + other,
        }
    }
}

// Addition scalar Assign
impl AddAssign<f32> for Vector3f {
    fn add_assign(&mut self, other: f32) {
        *self = Self {
            x: self.x + other,
            y: self.y + other,
            z: self.z + other,
        };
    }
}

// Subtraction
impl Sub for Vector3f {
    type Output = Self;

    fn sub(self, other: Self) -> Self {
        Vector3f {
            x: self.x - other.x,
            y: self.y - other.y,
            z: self.z - other.z,
        }
    }
}

// Subtraction Assign
impl SubAssign for Vector3f {
    fn sub_assign(&mut self, other: Self) {
        *self = Self {
            x: self.x - other.x,
            y: self.y - other.y,
            z: self.z - other.z,
        };
    }
}

// Subtraction scalar
impl Sub<f32> for Vector3f {
    type Output = Self;

    fn sub(self, other: f32) -> Self::Output {
        Self {
            x: self.x - other,
            y: self.y - other,
            z: self.z - other,
        }
    }
}

// Subtraction scalar Assign
impl SubAssign<f32> for Vector3f {
    fn sub_assign(&mut self, other: f32) {
        *self = Self {
            x: self.x - other,
            y: self.y - other,
            z: self.z - other,
        };
    }
}

// Multiplication
impl Mul for Vector3f {
    type Output = Vector3f;

    fn mul(self, other: Vector3f) -> Vector3f {
        Vector3f {
            x: self.x * other.x,
            y: self.y * other.y,
            z: self.z * other.z,
        }
    }
}

// Multiplication Assign
impl MulAssign for Vector3f {
    fn mul_assign(&mut self, other: Self) {
        *self = Self {
            x: self.x * other.x,
            y: self.y * other.y,
            z: self.z * other.z,
        };
    }
}

// Multiplication scalar
impl Mul<f32> for Vector3f {
    type Output = Self;

    fn mul(self, other: f32) -> Self::Output {
        Self {
            x: self.x * other,
            y: self.y * other,
            z: self.z * other,
        }
    }
}

// Multiplication scalar Assign
impl MulAssign<f32> for Vector3f {
    fn mul_assign(&mut self, other: f32) {
        *self = Self {
            x: self.x * other,
            y: self.y * other,
            z: self.z * other,
        };
    }
}

// Division
impl Div for Vector3f {
    type Output = Self;

    fn div(self, other: Vector3f) -> Self::Output {
        Self {
            x: self.x / other.x,
            y: self.y / other.y,
            z: self.z / other.z,
        }
    }
}

// Division Assign
impl DivAssign for Vector3f {
    fn div_assign(&mut self, other: Vector3f) {
        *self = Self {
            x: self.x / other.x,
            y: self.y / other.y,
            z: self.z / other.z,
        };
    }
}

// Division scalar
impl Div<f32> for Vector3f {
    type Output = Self;

    fn div(self, other: f32) -> Self::Output {
        Self {
            x: self.x / other,
            y: self.y / other,
            z: self.z / other,
        }
    }
}

// Division scalar Assign
impl DivAssign<f32> for Vector3f {
    fn div_assign(&mut self, other: f32) {
        *self = Self {
            x: self.x / other,
            y: self.y / other,
            z: self.z / other,
        };
    }
}

// Negation
impl Neg for Vector3f {
    type Output = Self;

    fn neg(self) -> Self::Output {
        Vector3f {
            x: -self.x,
            y: -self.y,
            z: -self.z,
        }
    }
}

impl Vector3f {
    pub fn new(x: f32, y: f32, z: f32) -> Vector3f {
        Vector3f { x, y, z }
    }

    // LengthSquared
    pub fn length_squared(&self) -> f32 {
        self.x * self.x + self.y * self.y + self.z * self.z
    }

    // dot product
    pub fn dot(&self, other: &Vector3f) -> f32 {
        self.x * other.x + self.y * other.y + self.z * other.z
    }

    // cross product
    pub fn cross(&self, other: &Vector3f) -> Vector3f {
        Vector3f {
            x: self.y * other.z - self.z * other.y,
            y: self.z * other.x - self.x * other.z,
            z: self.x * other.y - self.y * other.x,
        }
    }

    // length
    pub fn length(&self) -> f32 {
        f32::sqrt((self.x * self.x + self.y * self.y + self.z * self.z).into()).into()
    }

    // normalize
    pub fn normalize(&self) -> Vector3f {
        let len = self.length();
        Vector3f {
            x: self.x / len,
            y: self.y / len,
            z: self.z / len,
        }
    }
}

impl Vector3f {
    pub const ZERO: Vector3f = Vector3f {
        x: 0.0,
        y: 0.0,
        z: 0.0,
    };
    pub const IDENTITY: Vector3f = Vector3f {
        x: 1.0,
        y: 1.0,
        z: 1.0,
    };

    pub fn xy(&self) -> Vector2f {
        Vector2f::new(self.x, self.y)
    }
}

unsafe impl bytemuck::Pod for Vector3f {}
unsafe impl bytemuck::Zeroable for Vector3f {}
